package com.atguigu.daijia.driver.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.alibaba.nacos.common.utils.StringUtils;
import com.atguigu.daijia.common.constant.SystemConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.driver.config.TencentCloudProperties;
import com.atguigu.daijia.driver.mapper.*;
import com.atguigu.daijia.driver.service.DriverInfoService;
import com.atguigu.daijia.model.entity.driver.*;
import com.atguigu.daijia.model.form.driver.DriverFaceModelForm;
import com.atguigu.daijia.model.form.driver.UpdateDriverAuthInfoForm;
import com.atguigu.daijia.model.vo.driver.DriverAuthInfoVo;
import com.atguigu.daijia.model.vo.driver.DriverInfoVo;
import com.atguigu.daijia.model.vo.driver.DriverLoginVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tencentcloudapi.common.AbstractModel;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.iai.v20180301.IaiClient;
import com.tencentcloudapi.iai.v20180301.models.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class DriverInfoServiceImpl extends ServiceImpl<DriverInfoMapper, DriverInfo> implements DriverInfoService {

    @Resource
    private WxMaService wxMaService;
    @Resource
    private DriverInfoMapper driverInfoMapper;
    @Resource
    private DriverAccountMapper driverAccountMapper;
    @Resource
    private DriverLoginLogMapper driverLoginLogMapper;
    @Resource
    private DriverSetMapper driverSetMapper;
    @Resource
    private CosServiceImpl cosService;
    @Resource
    private TencentCloudProperties tencentCloudProperties;
    @Resource
    private DriverFaceRecognitionMapper driverFaceRecognitionMapper;

    /**
     * 根据用户登录码获取用户信息并处理登录
     * @param code 用户登录码
     * @return 返回用户的唯一标识ID
     */
    @Override
    public Long login(String code) {
        // 初始化openid变量
        String openId = null;
        try {
            // 使用wxMaService根据登录码获取会话信息
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            // 提取并保存openid
            openId = sessionInfo.getOpenid();
        } catch (WxErrorException e) {
            // 捕获微信错误异常并转换为运行时异常抛出
            throw new RuntimeException(e);
        }
        // 根据openid查询数据库以获取用户信息
        DriverInfo driverInfo = driverInfoMapper.selectOne(new LambdaQueryWrapper<DriverInfo>().
                eq(DriverInfo::getWxOpenId, openId));
        // 如果用户信息不存在，则创建新用户并初始化相关信息
        if(driverInfo==null){
            driverInfo = new DriverInfo();
            driverInfo.setWxOpenId(openId);
            // 使用当前时间戳作为默认昵称
            driverInfo.setNickname(String.valueOf(System.currentTimeMillis()));
            // 设置默认头像URL
            driverInfo.setAvatarUrl("https://wx2.sinaimg.cn/mw690/9516662fgy1h4atsqw5kbj20sv0sv7db.jpg");
            // 将新用户信息插入数据库
            driverInfoMapper.insert(driverInfo);

            // 初始化用户设置信息
            DriverSet driverSet = new DriverSet();
            driverSet.setDriverId(driverInfo.getId());
            // 设置默认接单距离为无限制
            driverSet.setOrderDistance(new BigDecimal(0));
            // 设置默认接单范围为5公里
            driverSet.setAcceptDistance(new BigDecimal(SystemConstant.ACCEPT_DISTANCE));
            // 设置默认自动接单选项为否
            driverSet.setIsAutoAccept(0);
            // 将用户设置信息插入数据库
            driverSetMapper.insert(driverSet);

            // 初始化用户账户信息
            DriverAccount driverAccount = new DriverAccount();
            driverAccount.setDriverId(driverInfo.getId());
            // 将用户账户信息插入数据库
            driverAccountMapper.insert(driverAccount);
        }

        // 记录登录日志
        DriverLoginLog driverLoginLog = new DriverLoginLog();
        driverLoginLog.setDriverId(driverInfo.getId());
        driverLoginLog.setMsg("小程序登录");
        driverLoginLogMapper.insert(driverLoginLog);
        // 返回用户ID
        return driverInfo.getId();
    }

    @Override
    public DriverLoginVo getDriverLoginInfo(Long driverId) {
        DriverInfo driverInfo = driverInfoMapper.selectById(driverId);
        DriverLoginVo driverLoginVo = new DriverLoginVo();
        BeanUtils.copyProperties(driverInfo,driverLoginVo);
        driverLoginVo.setIsArchiveFace(driverInfo.getFaceModelId()!=null);
        return driverLoginVo;
    }

    @Override
    public DriverAuthInfoVo getDriverAuthInfo(Long driverId) {
        DriverInfo driverInfo = driverInfoMapper.selectById(driverId);
        DriverAuthInfoVo driverAuthInfoVo = new DriverAuthInfoVo();
        BeanUtils.copyProperties(driverInfo,driverAuthInfoVo);
        driverAuthInfoVo.setIdcardBackShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardBackUrl()));
        driverAuthInfoVo.setIdcardFrontShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardFrontUrl()));
        driverAuthInfoVo.setIdcardHandShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardHandUrl()));
        driverAuthInfoVo.setDriverLicenseFrontShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseFrontUrl()));
        driverAuthInfoVo.setDriverLicenseBackShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseBackUrl()));
        driverAuthInfoVo.setDriverLicenseHandShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseHandUrl()));
        return driverAuthInfoVo;
    }

    @Override
    public Boolean updateDriverAuthInfo(UpdateDriverAuthInfoForm updateDriverAuthInfoForm) {
        DriverInfo driverInfo = new DriverInfo();
        driverInfo.setId(updateDriverAuthInfoForm.getDriverId());
        BeanUtils.copyProperties(updateDriverAuthInfoForm,driverInfo);
        return driverInfoMapper.updateById(driverInfo)!=0;
    }

    /**
     * 创建司机人脸模型
     * 该方法通过调用腾讯云的接口，在人脸库中创建一个人脸记录，并将该记录与司机信息关联
     *
     * @param driverFaceModelForm 包含司机人脸信息的表单，用于创建人脸模型
     * @return 如果成功创建人脸模型并关联到司机信息，返回true；否则返回false
     */
    @Override
    public Boolean creatDriverFaceModel(DriverFaceModelForm driverFaceModelForm) {
        // 根据提供的司机ID获取司机信息
        DriverInfo driverInfo = this.getById(driverFaceModelForm.getDriverId());

        try {
            // 初始化腾讯云API的访问凭证
            Credential cred = new Credential(tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey());
            // 设置HTTP请求配置，指定API访问域名
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 初始化客户端配置，包括HTTP请求配置
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 创建腾讯云人脸服务客户端
            IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion(), clientProfile);
            // 创建创建人脸记录的请求对象
            CreatePersonRequest req = new CreatePersonRequest();
            // 设置人脸库ID
            req.setGroupId(tencentCloudProperties.getPersonGroupId());
            // 设置人脸记录的唯一标识为人脸对应的司机ID
            req.setPersonId(String.valueOf(driverInfo.getId()));
            // 根据司机信息设置性别
            req.setGender(Long.parseLong(driverInfo.getGender()));
            // 设置质量控制和唯一性控制参数
            req.setQualityControl(4L);
            req.setUniquePersonControl(4L);
            // 设置人脸记录的名称为人脸对应的司机姓名
            req.setPersonName(driverInfo.getName());
            // 设置上传的人脸图片（Base64编码）
            req.setImage(driverFaceModelForm.getImageBase64());

            // 发送创建人脸记录的请求并获取响应
            CreatePersonResponse resp = client.CreatePerson(req);
            // 打印响应内容
            System.out.println(CreatePersonResponse.toJsonString(resp));
            // 如果成功创建人脸记录，则更新司机信息中的人脸模型ID和认证状态
            if (StringUtils.hasText(resp.getFaceId())) {
                driverInfo.setFaceModelId(resp.getFaceId());
                driverInfo.setAuthStatus(2);
                // 保存更新后的司机信息
                driverInfoMapper.updateById(driverInfo);
            }
        } catch (TencentCloudSDKException e) {
            // 如果发生异常，打印异常信息并返回false
            System.out.println(e.toString());
            return false;
        }
        // 如果一切操作成功，返回true
        return true;
    }

    @Override
    public DriverSet getDriverSet(Long driverId) {
        return driverSetMapper.selectOne(new LambdaQueryWrapper<DriverSet>().eq(DriverSet::getDriverId,driverId));
    }

    @Override
    public Boolean isFaceRecognition(Long driverId) {
        Long count = driverFaceRecognitionMapper.selectCount(new LambdaQueryWrapper<DriverFaceRecognition>().
                eq(DriverFaceRecognition::getDriverId, driverId).
                eq(DriverFaceRecognition::getFaceDate, new DateTime().toString("yyyy-MM-dd")));
        return count != 0;
    }

    /**
     * 验证司机人脸
     *
     * @param driverFaceModelForm 司机人脸模型表单，包含司机ID和人脸图像的Base64编码
     * @return 如果人脸匹配且活体检测通过，则返回true，否则返回false
     * 该方法通过调用腾讯云的接口来验证司机的人脸是否匹配，主要步骤包括：
     * 1. 使用腾讯云的密钥信息创建认证凭证
     * 2. 设置HTTP配置文件和客户端配置文件
     * 3. 创建腾讯云IAI（人脸智能）客户端
     * 4. 使用司机的人脸图像和ID发起人脸验证请求
     * 5. 如果人脸匹配，则进一步进行活体检测
     * 6. 如果活体检测通过，则记录司机的人脸识别信息到数据库，并返回true
     * 7. 如果匹配或活体检测失败，则打印响应信息
     * 8. 如果发生异常，则抛出自定义异常，表示人脸识别失败
     */
    @Override
    public Boolean verifyDriverFace(DriverFaceModelForm driverFaceModelForm) {
        try{
            // 创建认证凭证
            Credential cred = new Credential(tencentCloudProperties.getSecretId(),
                    tencentCloudProperties.getSecretKey());
            // 设置HTTP配置文件
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 设置客户端配置文件
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 创建腾讯云IAI客户端
            IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion(), clientProfile);
            // 初始化人脸验证请求
            VerifyFaceRequest req = new VerifyFaceRequest();
            // 设置请求中的人脸图像数据
            req.setImage(driverFaceModelForm.getImageBase64());
            // 设置请求中的司机ID
            req.setPersonId(String.valueOf(driverFaceModelForm.getDriverId()));

            // 发起人脸验证请求并获取响应
            VerifyFaceResponse resp = client.VerifyFace(req);
            // 如果人脸匹配
            if(resp.getIsMatch()){
                // 进行活体检测
                if(detectLiveFace(driverFaceModelForm.getImageBase64())){
                    // 创建司机人脸识别记录
                    DriverFaceRecognition driverFaceRecognition = new DriverFaceRecognition();
                    // 设置司机ID
                    driverFaceRecognition.setDriverId(driverFaceModelForm.getDriverId());
                    // 设置人脸识别日期
                    driverFaceRecognition.setFaceDate(new Date());
                    // 插入司机人脸识别记录到数据库
                    driverFaceRecognitionMapper.insert(driverFaceRecognition);
                    // 返回验证成功
                    return true;
                }
            }
            // 打印响应信息
            System.out.println(AbstractModel.toJsonString(resp));
        } catch (TencentCloudSDKException e) {
            // 打印异常信息
            System.out.println(e.toString());
        }
        // 抛出自定义异常，表示人脸识别失败
        throw new GuiguException(ResultCodeEnum.FACE_FAIL);
    }

    private boolean detectLiveFace(String imageBase64) {
        try{
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey());
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion(), clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DetectLiveFaceRequest req = new DetectLiveFaceRequest();
            req.setImage(imageBase64);
            // 返回的resp是一个DetectLiveFaceResponse的实例，与请求对象对应
            DetectLiveFaceResponse resp = client.DetectLiveFace(req);
            // 输出json格式的字符串回包
            System.out.println(DetectLiveFaceResponse.toJsonString(resp));
            if(resp.getIsLiveness()) {
                return true;
            }
        } catch (TencentCloudSDKException e) {
            System.out.println(e.toString());
        }
        return false;
    }

    @Override
    public Boolean updateServiceStatus(Long driverId, Integer status) {
        driverSetMapper.update(null, new LambdaUpdateWrapper<DriverSet>().
                eq(DriverSet::getDriverId, driverId).set(DriverSet::getServiceStatus, status));
        return true;
    }

    @Override
    public DriverInfoVo getDriverInfo(Long driverId) {
        DriverInfo driverInfo = driverInfoMapper.selectById(driverId);
        DriverInfoVo driverInfoVo = new DriverInfoVo();
        BeanUtils.copyProperties(driverInfo, driverInfoVo);
        driverInfoVo.setDriverLicenseAge(new DateTime().getYear() -
                new DateTime(driverInfo.getDriverLicenseIssueDate()).getYear() + 1);

        return driverInfoVo;
    }

    @Override
    public String getDriverOpenId(Long driverId) {
        return driverInfoMapper.selectOne(new LambdaQueryWrapper<DriverInfo>().eq(DriverInfo::getId, driverId)).getWxOpenId();
    }
}